package com.sinotrans.common.aop;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;

import com.google.gson.Gson;
import com.sinotrans.common.entity.SecurityProperties;
import com.sinotrans.common.entity.expand.DefaultProperties;
import com.sinotrans.common.entity.expand.IgnoreProperties;
import com.sinotrans.common.exception.BusinessException;
import com.sinotrans.common.security.AuthEntity;
import com.sinotrans.common.security.AuthUtils;
import com.sinotrans.common.security.DigestSignature;
import com.sinotrans.common.util.ServletUtil;
import com.sinotrans.common.util.Utils;
import com.sinotrans.logger.util.Util;

/**
 * 请求角色权限校验
 * 
 * @author gaofu
 *
 */
public class RoleAspect extends ControllerAspect {
	private DigestSignature digestSignature = null;
	private SecurityProperties securityProperties;

	/**
	 * 默认角色
	 */
	private volatile Collection<String> defaultRoles = null;
	private volatile AntPathMatcher ingorePattern = null;

	private Object lock = new Object();

	protected Gson gson = Utils.getGson();

	public RoleAspect(DigestSignature digestSignature) throws BusinessException {
		if (digestSignature == null) {
			throw new BusinessException("digestSignature is null");
		}
		this.digestSignature = digestSignature;
		this.securityProperties = digestSignature.getSecurityConfig();
		if (securityProperties.getIgnore() == null) {
			securityProperties.setIgnore(new IgnoreProperties());
		}
		if (securityProperties.getDefaults() == null) {
			securityProperties.setDefaults(new DefaultProperties());
		}
	}

	@Override
	protected void controllerVerify(Class<?> targetClass, Method targetMethod) throws Exception {
		boolean isAnonymous = true;
		AuthEntity authEntity = new AuthEntity();
		HttpServletRequest request = ServletUtil.getRequest();
		try {
			String authinfo = AuthUtils.getAuthinfo(request);
			if (!StringUtils.isEmpty(authinfo)) {
				authEntity = decode(authinfo);
				request.setAttribute(AuthUtils.authorize, authEntity);
				if (!StringUtils.isEmpty(authEntity.getUid())) {
					isAnonymous = false;
				}
			}
		} catch (Exception e) {
			logger.error("decode authEntity error.", e);
		}
		Annotation[] classAnnotations = targetClass.getAnnotations();
		Annotation[] methodAnnotations = targetMethod.getAnnotations();
		// 不是系统内部调用
		if (!AuthUtils.True.equals(authEntity.getIsd())) {
			// 匿名访问
			if (isAnonymous) {
				verifyAllowAnonymous(classAnnotations, methodAnnotations);
			} else {
				// 角色校验
				Collection<String> rolesAllowed = allowedRoles(classAnnotations, methodAnnotations);
				rolesAllowed.addAll(getDefaultRoles());
				if (!rolesAllowed.isEmpty()) {// 为空认为所有的角色都可以访问
					boolean denied = true;
					if (authEntity.getRls() != null) {
						for (String role : authEntity.getRls()) {
							if (rolesAllowed.contains(role)) {
								denied = false;
								break;
							}
						}
						if (denied) {
							throw accessDeniedException();
						}
					}
				}
			}
		}
	}

	/**
	 * 是否是忽略校权的资源
	 * 
	 * @param request
	 * @return
	 */
	public boolean isIngoreUri(HttpServletRequest request) {
		String servletPath = request.getServletPath();
		boolean isIngore = isIngoreUri(servletPath);
		return isIngore;
	}

	/**
	 * 是否是忽略校权的url
	 * 
	 * @param httpRequest
	 * @return
	 */
	public boolean isIngoreUri(String url) {
		List<String> urls = securityProperties.getIgnore().getUrls();
		if (!Util.isEmpty(urls)) {
			boolean matching = false;
			for (String pattern : urls) {
				matching = patternMatcher().match(pattern, url);
				if (matching) {
					return true;
				}
			}
		}
		return false;
	}

	protected AntPathMatcher patternMatcher() {
		if (ingorePattern == null) {
			synchronized (lock) {
				if (ingorePattern == null) {
					ingorePattern = new AntPathMatcher();
				}
			}
		}
		return ingorePattern;
	}

	/**
	 * 获取默认角色
	 * 
	 * @return
	 */
	protected Collection<String> getDefaultRoles() {
		if (defaultRoles == null) {
			synchronized (this) {
				if (defaultRoles == null) {
					defaultRoles = new ArrayList<String>();
					String defaultRole = securityProperties.getDefaults().getRoles();
					if (!StringUtils.isEmpty(defaultRole)) {
						String[] roleArray = defaultRole.split(",");
						defaultRoles.addAll(Arrays.asList(roleArray));
					}
				}
			}
		}
		return defaultRoles;
	}

	/**
	 * 匿名访问校验
	 * 
	 * @param classAnnotations
	 * @param methodAnnotations
	 * @return
	 * @throws Exception
	 */
	protected void verifyAllowAnonymous(Annotation[] classAnnotations, Annotation[] methodAnnotations)
			throws Exception {
		// 匿名访问
		HttpServletRequest request = ServletUtil.getRequest();
		String authinfo = AuthUtils.getAuthinfo(request);
		boolean isAllow = StringUtils.isEmpty(authinfo) || Utils.isPermitAll(classAnnotations)
				|| Utils.isPermitAll(methodAnnotations) || isIngoreUri(request);
		if (!isAllow) {
			throw accessDeniedException();
		}
	}

	/**
	 * 读取{@link javax.annotation.security.RolesAllowed} 注解的值
	 * 
	 * @param handler
	 * @return
	 */
	protected Collection<String> allowedRoles(Annotation[] classAnnotations, Annotation[] methodAnnotations) {
		List<String> roles = new ArrayList<>();
		Collection<String> methodRoles = Utils.allowedRoles(methodAnnotations);
		if (methodRoles != null && !methodRoles.isEmpty()) {
			roles.addAll(methodRoles);
		} else {
			Collection<String> classRoles = Utils.allowedRoles(methodAnnotations);
			if (classRoles != null && !classRoles.isEmpty()) {
				roles.addAll(classRoles);
			}
		}
		return roles;
	}

	/**
	 * 用户信息解码
	 * 
	 * @param authinfo
	 * @return
	 * @throws Exception
	 */
	protected AuthEntity decode(String authinfo) throws Exception {
		return digestSignature.decode(authinfo, AuthEntity.class);
	}

	/**
	 * access denied Exception
	 * 
	 * @return
	 */
	protected Exception accessDeniedException() {
		return new BusinessException("access.denied");
	}

}
